package probe

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httptrace"
	"simple-http-probe/config"
	"strings"
	"time"
)

func msDurationStr(d time.Duration) string {
	return fmt.Sprintf("%dms", int(d/time.Millisecond))
}

func DoHttpProbe(url string) string {
	twSec := config.GlobalTwSec

	// 结果string
	dnsStr := ""
	targetAddr := ""
	// 时间对象
	var t0, t1, t2, t3, t4 time.Time
	// 全局或出错使用
	startT := time.Now()
	// 初始化http req对象
	req, _ := http.NewRequest("GET", url, nil)

	trace := &httptrace.ClientTrace{
		DNSStart: func(_ httptrace.DNSStartInfo) {
			t0 = time.Now()
		},
		DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
			t1 = time.Now()
			ips := make([]string, 0)
			for _, d := range dnsInfo.Addrs {
				ips = append(ips, d.IP.String())
			}
			dnsStr = strings.Join(ips, ",")
		},
		ConnectStart: func(network, addr string) {
			if t1.IsZero() {
				t1 = time.Now()
			}
		},
		ConnectDone: func(network, addr string, err error) {
			if err != nil {
				log.Printf("无法建立和探测目标的连接, addr: %v, err: %v", addr, err)
				return
			}
			targetAddr = addr
			t2 = time.Now()
		},

		GotConn: func(_ httptrace.GotConnInfo) {
			t3 = time.Now()
		},
		GotFirstResponseByte: func() {
			t4 = time.Now()
		},
	}
	req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
	client := http.Client{
		Timeout: time.Duration(twSec) * time.Second,
	}
	resp, err := client.Do(req)
	if err != nil {
		msg := fmt.Sprintf("[http探测出错]\n" +
			"[http探测目标:%s]\n" +
			"[错误详情:%s]\n" +
			"[总耗时:%s]\n",
			url,
			err,
			msDurationStr(time.Now().Sub(startT)),
			)
		log.Printf(msg)
		return msg
	}
	defer  resp.Body.Close()
	endT := time.Now()
	if t0.IsZero() {
		t0 = t1
	}
	dnsLookup := msDurationStr(t1.Sub(t0))
	tcpConn := msDurationStr(t3.Sub(t1))
	serverProc := msDurationStr(t4.Sub(t3))
	total := msDurationStr(endT.Sub(t0))
	probeResStr := fmt.Sprintf(
		"[http探测目标：%s]\n" +
		"[dns解析结果：%s]\n" +
		"[连接地址和端口：%s]\n" +
		"[状态码：%d]\n" +
		"[dns解析耗时：%s]\n" +
		"[tcp连接耗时：%s]\n" +
		"[服务端处理耗时：%s]\n" +
		"[总耗时：%s]\n" ,
		url,
		dnsStr,
		targetAddr,
		resp.StatusCode,
		dnsLookup,
		tcpConn,
		serverProc,
		total,
		)
	return probeResStr
}
